// Copyright (C) 2010 Choonghwan Lee (linjus.lee@gmail.com)
//
// This file is part of amy (http://unitcvt.codeplex.com/).
//
// amy is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// amy is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with amy. If not, see <http://www.gnu.org/licenses/>.


function Unit(id, name, symbol, wikiitem, expr){
	this.id = id;
	this.name = name;
	this.symbol = symbol;
	if (wikiitem == null) {
		this.infourl = "http://www.google.com/search?q=" + name;
		this.infoicon = "images/unitinfo-google.png";
	}
	else if (wikiitem.indexOf("http://") == 0) {
		this.infourl = wikiitem;
		this.infoicon = "images/unitinfo.png";
	}
	else {
		this.infourl = "http://en.wikipedia.org/wiki/" + wikiitem;
		this.infoicon = "images/unitinfo-wikipedia.png";
	}
	this.expr = expr;
	this.visible = true;
}

function UnitCategory(id, name, units) {
	this.id = id;
	this.name = name;
	this.units = units;
}

UnitCategory.prototype.instantiateUnitValues = function() {
	var values = new Array();
	for (var i = 0; i < this.units.length; ++i) {
		var unit = this.units[i];
		if (unit.visible)
			values.push(new UnitValue(unit));
	}
	return values;
}

UnitCategory.prototype.convert = function(source, targets) {
	var sivalue;

	if (source.unit.expr == null)
		sivalue = source.value;
	else {
		if (source.unit.id == "light_nm")
			sivalue = 299792458000000000 / source.value;
		else {
			var postfix = new Postfix();
			sivalue = postfix.inverse(source.unit.expr, source.value);
		}
	}

	for (var i = 0; i < targets.length; ++i) {
		var target = targets[i];
		if (target.unit.id == source.unit.id) continue;

		if (sivalue == null || sivalue.length == 0 || isNaN(sivalue))
			target.value = null;
		else if (target.unit.expr == null)
			target.value = sivalue;
		else {
			var postfix = new Postfix();
			target.value = postfix.convert(target.unit.expr, sivalue);
		}
	}
}

function UnitInformation() {
}

var unitInformation = new UnitInformation();

UnitInformation.prototype.initialize = function() {
	this.categories = new Array();

	// Most expressions came from the following URL:
	// http://en.wikipedia.org/wiki/Conversion_of_units
	klingonpage = "http://klingon.wikia.com/wiki/juvmeH_%27op";

	// Currently, the expression should refer to only SI unit
	// because the name is not actually interpreted. Any string
	// is assumed to be the SI unit value.
	// To get a correct value by inverse, the SI unit value should be
	// located before the other operand; e.g., m 10 /, not 10 m /.

	// Unit(ID, name, symbol, Wikipedia entry, formula)

	//  LENGTH
	{
		var units = new Array();
		units.push(new Unit("m", "meter", "m", "Metre", null));
		units.push(new Unit("ang", "Ångström", "Å","Ångström", "m 100000 100000 * *"))
		units.push(new Unit("ft", "foot (international)", "ft (int)", "Foot_(length)", "m 0.3048 /"));
		units.push(new Unit("ft_us", "foot (U.S. survey)", "ft (US)", "Foot_(length)", "m 0.304800610 /"));
		units.push(new Unit("in", "inch", "in", "Inch", "m 0.0254 /"));
		units.push(new Unit("ft_in", "foot inch", "ft'in\"", "Foot_(length)", "m 0.3048 / ; ' 12 * ; \""));
		units.push(new Unit("chain", "chain", "ch", "Chain_(unit)", "m 20.11684 /"));
		units.push(new Unit("mi", "mile", "mi", "Mile", "m 1609.344 /"));
		units.push(new Unit("mi_us", "mile (U.S. survey)", "mi (surv)", "Mile", "m 1609.347219 /"));
		units.push(new Unit("nauticalmile", "nautical mile (international)", "nmi", "Nautical_mile", "m 1852 /"));
		units.push(new Unit("yd", "yard", "yd", "Yard", "m 0.9144 /"));
		units.push(new Unit("astrounit", "astronomical unit", "AU", "Astronomical_unit", "m 149597871464 /"));
		units.push(new Unit("lightyear", "light-year", "l.y.", "Light_year", "m 9.4607304725808 1000000000000000 * /"));
		units.push(new Unit("lightsec", "light-second", "l.s.", "Light_second", "m 299792458 /"));
		units.push(new Unit("point_us", "point (US)", "pt", "Point_(typography)", "m 0.000351450 /"));
		units.push(new Unit("ri", "리 (Korea)", "리", "Li_(length)", "m 392.72 /"));
		units.push(new Unit("qelIqam", "qelI'qam", "qelI'qam", klingonpage, "m 2000 /"));
		units.push(new Unit("loghqam", "loghqam", "loghqam", klingonpage, "m 11.825913090726 1000000000000000 * /"));
		units.push(new Unit("uj", "'uj", "'uj", klingonpage, "m 0.3483 /"));
		this.categories.push(new UnitCategory("length", "Length", units));
	}

	//  AREA
	{
		var units = new Array();
		units.push(new Unit("sq_m", "square meter", "m<sup>2</sup>", "Square_metre", null));
		units.push(new Unit("acre", "acre (international)", "ac (int)", "Acre", "sq_m 4046.8564224 /"));
		units.push(new Unit("acre_us", "acre (U.S. survey)", "ac (surv)", "Acre", "sq_m 4046.873 /"));
		units.push(new Unit("hectare", "hectare", "ha", "Hectare", "sq_m 10000 /"));
		units.push(new Unit("sq_foot", "square foot", "sq ft", "Square_foot", "sq_m 9.290304 0.01 * /"));
		units.push(new Unit("sq_foot_us", "square foot (U.S. survey)", "sq ft", "Square_foot", "sq_m 9.29034116132749 0.01 * /"));
		units.push(new Unit("sq_in", "square inch", "sq in", "Square_inch", "sq_m 6.4516 0.0001 * /"));
		units.push(new Unit("sq_mile", "square mile", "sq mi", null, "sq_m 2.589988110336 1000000 * /"));
		units.push(new Unit("sq_mile_us", "square mile (U.S. survey)", "sq mi", null, "sq_m 2.589998 1000000 * /"));
		units.push(new Unit("sq_yard", "square yard", "sq yd", "Square_yard", "sq_m 0.83612736 /"));
		units.push(new Unit("pyeong", "평", "평", "Pyeong", "sq_m 3.3058 /"));
		this.categories.push(new UnitCategory("area", "Area", units));
	}

	//  VOLUME
	{
		var units = new Array();
		units.push(new Unit("cubic_m", "cubic meter", "m<sup>3</sup>", "Cubic_metre", null));
		units.push(new Unit("acre_foot", "acre-foot", "ac ft", "Acre-foot", "cubic_m 1233.48183754752 /"));
		units.push(new Unit("barrel_imp", "barrel (Imperial)", "bl(Imp)", "Barrel_(unit)", "cubic_m 0.16365924 /"));
		units.push(new Unit("barrel_petro", "barrel (petroleum)", "bbl", null, "cubic_m 0.158987294928 /"));
		units.push(new Unit("barrel_us_dry", "barrel (U.S. dry)", "bl(US)", null, "cubic_m 0.115628198985075 /"));
		units.push(new Unit("barrel_us_fluid", "barrel (U.S. fluid)", "fl bl(US)", null, "cubic_m 0.119240471196 /"));
		units.push(new Unit("cubic_foot", "cubic foot", "cu ft", "Cubic_foot", "cubic_m 0.028316846592 /"));
		units.push(new Unit("cubic_inch", "cubic inch", "cu in", null, "cubic_m 16.387064 0.000001 * /"));
		units.push(new Unit("cubic_mile", "cubic mile", "cu mi", null, "cubic_m 4168181825.440579584 /"));
		units.push(new Unit("cubic_yard", "cubic yard", "cu yd", null, "cubic_m 0.764554857984 /"));
		units.push(new Unit("cup_metric", "cup (metric)", "c", null, "cubic_m 250.0 0.000001 * /"));
		units.push(new Unit("cup_us_food", "cup (U.S. food nutrition)", "c(US)", null, "cubic_m 2.4 0.0001 * /"));
		units.push(new Unit("fluid_ounce_imp", "fluid ounce (Imperial)", "fl oz(Imp)", "Fluid_ounce", "cubic_m 28.4130625 0.000001 * /"));
		units.push(new Unit("fluid_ounce_us", "fluid ounce (U.S. customary)", "US fl oz", null, "cubic_m 29.5735295625 0.000001 * /"));
		units.push(new Unit("fluid_ounce_us_food", "fluid ounce (U.S. food nutrition labeling)", "US fl oz", null, "cubic_m 3 0.00001 * /"));
		units.push(new Unit("gallon_beer", "gallon (beer)", "beer gal", "Gallon", "cubic_m 4.621152048 0.001 * /"));
		units.push(new Unit("gallon_imp", "gallon (Imperial)", "gal(Imp)", "Gallon", "cubic_m 4.54609 0.001 * /"));
		units.push(new Unit("gallon_us_dry", "gallon (U.S. dry)", "gal(US)", "Gallon", "cubic_m 4.40488377086 0.001 * /"));
		units.push(new Unit("gallon_us_fluid", "gallon (U.S. fluid)", "gal(US)", "Gallon", "cubic_m 3.785411784 0.001 * /"));
		units.push(new Unit("pint_imp", "pint (Imperial)", "pt(Imp)", "Pint", "cubic_m 568.26125 0.000001 * /"));
		units.push(new Unit("pint_us_dry", "pint (U.S. dry)", "pt(US dry)", "Pint", "cubic_m 550.6104713575 0.000001 * /"));
		units.push(new Unit("pint_us_fluid", "pint (U.S. fluid)", "pt(US fl)", "Pint", "cubic_m 473.176473 0.000001 * /"));
		units.push(new Unit("quart_imp", "quart (Imperial)", "qt(Imp)", "Quart", "cubic_m 1.1365225 0.001 * /"));
		units.push(new Unit("quart_us_dry", "quart (U.S. dry)", "qt(US)", "Quart", "cubic_m 1.101220942715 0.001 * /"));
		units.push(new Unit("quart_us_fluid", "quart (U.S. fluid)", "qt(US)", "Quart", "cubic_m 946.352946 0.000001 * /"));
		units.push(new Unit("liter", "liter", "L", "Litre", "cubic_m 0.001 /"));
		units.push(new Unit("tablespoon_ca", "tablespoon (Canadian)", "tbsp", "Tablespoon", "cubic_m 14.20653125 0.000001 * /"));
		units.push(new Unit("tablespoon_imp", "tablespoon (Imperial)", "tbsp", "Tablespoon", "cubic_m 17.7581640625 0.000001 * /"));
		units.push(new Unit("tablespoon_metric", "tablespoon (metric)", "tbsp", "Tablespoon", "cubic_m 15.0 0.000001 * /"));
		units.push(new Unit("tablespoon_us", "tablespoon (U.S. customary)", "tbsp", "Tablespoon", "cubic_m 14.7867647825 0.000001 * /"));
		units.push(new Unit("tablespoon_us_food", "tablespoon (U.S. food nutrition labeling)", "tbsp", "Tablespoon", "cubic_m 1.5 0.00001 * /"));
		units.push(new Unit("teaspoon_ca", "teaspoon (Canadian)", "tsp", "Teaspoon", "cubic_m 4.735510416 0.000001 * /"));
		units.push(new Unit("teaspoon_imp", "teaspoon (Imperial)", "tsp", "Teaspoon", "cubic_m 5.91938802083 0.000001 * /"));
		units.push(new Unit("teaspoon_metric", "teaspoon (metric)", "tsp", "Teaspoon", "cubic_m 5.0 0.000001 * /"));
		units.push(new Unit("teaspoon_us", "teaspoon (U.S. customary)", "tsp", "Teaspoon", "cubic_m 4.928921595 0.000001 * /"));
		units.push(new Unit("teaspoon_us_food", "teaspoon (U.S. food nutrition labeling)", "tsp", "Teaspoon", "cubic_m 5 0.000001 * /"));
		units.push(new Unit("tlhoren", "tlho'ren", "tlho'ren", klingonpage, "cubic_m 0.000973 /"));
		this.categories.push(new UnitCategory("volume", "Volume", units));
	}

	//  MASS
	{
		var units = new Array();
		units.push(new Unit("kg", "kilogram", "kg", "Kilogram", null));
		units.push(new Unit("ounce_av", "ounce (avoirdupois)", "oz (av)", "Ounce", "kg 28.349523125 0.001 * /"));
		units.push(new Unit("ounce_us_food", "ounce (U.S. food nutrition labeling)", "oz", "Ounce", "kg 28 0.001 * /"));
		units.push(new Unit("pound_av", "pound (avoirdupois)", "lb (av)", "Pound_(mass)", "kg 0.45359237 /"));
		units.push(new Unit("slug", "slug (geepound)", "slug", "slug_(mass)", "kg 14.593903 /"))
		units.push(new Unit("stone", "stone","st", "Stone_(weight)", "kg 6.35029318 /"))
		units.push(new Unit("pound_metric", "pound (metric)", "lb", "Pound_(mass)", "kg 500 0.001 * /"));
		units.push(new Unit("tonne", "tonne", "t", "Tonne", "kg 1000 /"));
		units.push(new Unit("bag", "bag (coffee)", "bag", null, "kg 60 /"));
		units.push(new Unit("cheb", "cheb", "cheb", klingonpage, "kg 2.25 /"));
		this.categories.push(new UnitCategory("mass", "Mass", units));
	}

	//  TIME
	{
		var units = new Array();
		units.push(new Unit("s", "second", "s", "Second", null));
		units.push(new Unit("min", "minute", "min", "Second", "s 60 /"));
		units.push(new Unit("h", "hour", "h", "Hour", "s 3600 /"));
		units.push(new Unit("d", "day", "d", "Day", "s 86400 /"));
		units.push(new Unit("d_sidereal", "day (sidereal)", "d", "Day", "s 86164.1 /"));
		units.push(new Unit("week", "week", "wk", "Week", "s 604800 /"));
		units.push(new Unit("year_gregorian", "year (Gregorian)", "yr", "Year", "s 31556952 /"));
		units.push(new Unit("year_julian", "year (Julian)", "yr", "Year", "s 31557600 /"));
		units.push(new Unit("year_sidereal", "year (sidereal)", "yr", "Year", "s 31558149.7632 /"));
		units.push(new Unit("year_tropical", "year (tropical)", "yr", "Year", "s 31556925 /"));
		this.categories.push(new UnitCategory("time", "Time", units));
	}

	//  SPEED
	{
		var units = new Array();
		units.push(new Unit("m_p_s", "meter per second", "m/s", "Metre_per_second", null));
		units.push(new Unit("km_p_s", "kilometer per hour", "km/h", "Kilometre_per_hour", "m_p_s 2.777778 0.1 * /"));
		units.push(new Unit("knot", "knot", "kn", "Knot_(speed)", "m_p_s 0.514444 /"));
		units.push(new Unit("knot_admiralty", "knot (Admiralty)", "kn", "Knot_(speed)", "m_p_s 0.514773 /"));
		units.push(new Unit("mph", "mile per hour", "mph", "Mile_per_hour", "m_p_s 0.44704 /"));
		units.push(new Unit("mps", "mile per second", "mps", null, "m_p_s 1609.344 /"));
		units.push(new Unit("light_speed", "speed of light in vacuum", "c", "Speed_of_light", "m_p_s 299792458 /"));
		units.push(new Unit("sound_speed", "speed of sound in air", "s", "Speed_of_sound", "m_p_s 344 /"));
		this.categories.push(new UnitCategory("speed", "Speed", units));
	}
	
	//  FORCE
	{
		var units = new Array();
		units.push(new Unit("newton", "newton", "N", "Newton_(unit)", null));
		units.push(new Unit("kgf", "kilogram-force", "kgf", "Kilogram-force", "N 9.80665 /"));
		units.push(new Unit("dyne", "dyne", "dyn", "Dyne", "N 100000 *"));
		units.push(new Unit("kip", "kip", "kip", "Kip_(unit)", "N 4.4482216152605 1000 * /"));
		units.push(new Unit("ozf", "ounce-force", "ozf", "Ounce-force", "N 0.2780138509537812 /"));
		units.push(new Unit("lbf", "pound-force", "lbf", "Ounce-force", "N 4.4482216152605 /"));
		units.push(new Unit("pdl", "poundal", "pdl", "Poundal", "N 0.138254954376 /"));
		this.categories.push(new UnitCategory("force", "Force", units));
	}

	//  TORQUE
	{
		var units = new Array();
		units.push(new Unit("Nm", "Newton meter", "N·m", "Newton_metre", null));
		units.push(new Unit("ft_lbf", "foot-pound force", "ft lbf", "Foot-pound_force", "Nm 1.3558179483314004 /"));
		units.push(new Unit("ft_pdl", "foot-poundal", "ft pdl", null, "Nm 4.21401100938048 0.01 * /"));
		units.push(new Unit("in_lbf", "inch-pound force", "in lbf", "Inch-pound_force", "Nm 0.1129848290276167 /"));
		units.push(new Unit("m_kg", "meter kilogram", "m kg", null, "Nm 0.101971621 /"));
		this.categories.push(new UnitCategory("torque", "Torque", units));
	}

	//  PRESSURE
	{
		var units = new Array();
		units.push(new Unit("pascal", "pascal", "Pa", "Pascal_(unit)", null));
		units.push(new Unit("atmosphere_std", "atmosphere (standard)", "atm", "Atmosphere_(unit)", "pascal 101325 /"));
		units.push(new Unit("atmosphere_tech", "atmosphere (technical)", "at", "Technical_atmosphere", "pascal 98066.5 /"));
		units.push(new Unit("bar", "bar", "bar", "Bar_(unit)", "pascal 100000 /"));
		units.push(new Unit("fthg", "foot of mercury", "ft<sub>Hg</sub>", null, "pascal 40636.66 /"));
		units.push(new Unit("fth2o", "foot of water", "ftH<sub>2</sub>O", null, "pascal 2988.98 /"));
		units.push(new Unit("inmg", "inch of mercury", "in<sub>Hg</sub>", "Inch_of_mercury", "pascal 3386.389 /"));
		units.push(new Unit("inh2o", "inch of water", "inH<sub>2</sub>O", "Inch_of_water", "pascal 249.082 /"));
		units.push(new Unit("mmhg", "millimeter of mercury", "mmHg", "Torr", "pascal 133.3224 /"));
		units.push(new Unit("mmh2o", "millimeter of water", "mmH<sub>2</sub>O", "Torr", "pascal 9.80638 /"));
		units.push(new Unit("ksi", "kip per square inch", "ksi", null, "pascal 6894757 /"));
		units.push(new Unit("psf", "pound per square foot", "psf", null, "pascal 47.88025 /"));
		units.push(new Unit("psi", "pound per square inch", "psi", "Pound-force_per_square_inch", "pascal 6.894757 1000 * /"));
		units.push(new Unit("torr", "Torr", "Torr", "Torr", "pascal 133.3224 /"));
		this.categories.push(new UnitCategory("pressure", "Pressure", units));
	}

	//  ENERGY
	{
		var units = new Array();
		units.push(new Unit("joule", "joule", "J", "Joule", null));
		units.push(new Unit("btu_iso", "British thermal unit (ISO)", "BTU<sub>ISO</sub>", "British_thermal_unit", "joule 1.0545 1000 * /"));
		units.push(new Unit("calorie_it", "calorie (International Table)", "cal<sub>IT</sub>", "Calorie", "joule 4.1868 /"));
		units.push(new Unit("calorie_mean", "calorie (mean)", "cal<sub>mean</sub>", "Calorie", "joule 4.19002 /"));
		units.push(new Unit("calorie_therm", "calorie (thermochemical)", "cal<sub>th</sub>", "Calorie", "joule 4.184 /"));
		units.push(new Unit("ev", "electronvolt", "eV", "Electron_volt", "joule 1.60217733 0.0000000000000000001 * /"));
		units.push(new Unit("kilowatthour", "kilowatt-hour", "kW·h", "Kilowatt-hour", "joule 3.6 1000000 * /"));
		units.push(new Unit("hp_h", "horsepower-hour", "hp-h", null,"J 2684519.537696172792 /"))
		this.categories.push(new UnitCategory("energy", "Energy", units));
	}
	
	//  POWER
	{
		var units = new Array();
		units.push(new Unit("watt", "Watt", "W", "Watt", null));
		units.push(new Unit("btu_p_h", "BTU (International Table) per hour", "BTU<sub>IT</sub>/h", null, "W 0.293071 /"));
		units.push(new Unit("btu_p_m", "BTU (International Table) per minute", "BTU<sub>IT</sub>/min", null, "W 17.584264 /"));
		units.push(new Unit("btu_p_s", "BTU (International Table) per second", "BTU<sub>IT</sub>/s", null, "W 1055.05585262 /"));
		units.push(new Unit("calorie_p_s", "calorie (International Table) per second", "cal<sub>IT</sub>/s", null, "W 4.1868 /"));
		units.push(new Unit("ftlb_p_h", "foot-pound-force per hour", "ft·lbf/h", null, "W 3.766161 0.0001 * /"));
		units.push(new Unit("ftlb_p_m", "foot-pound-force per minute", "ft·lbf/min", null, "W 0.02259696580552334 /"));
		units.push(new Unit("ftlb_p_s", "foot-pound-force per second", "ft·lbf/s", null, "W 1.3558179483314004 /"));
		units.push(new Unit("bhp", "Boiler Horsepower", "bhp", null, "W 9810.657 /"));
		units.push(new Unit("horsepower", "Horsepower (Imperial electrical)", "hp", "Horsepower", "W 746 /"));
		units.push(new Unit("ton_ac", "ton of air conditioning", "ton (ice)", "Ton", "W 3504 /"));
		this.categories.push(new UnitCategory("power", "Power", units));
	}

	//  ACCELERATION
	{
		var units = new Array();
		units.push(new Unit("m_p_s_s", "meter per second squared", "m/s<sup>2</sup>", "Meter_per_second_squared", null));
		units.push(new Unit("ft_p_h_p_s", "feet per hour per second", "fph/s", null, "m_p_s_s 8.466667 0.00001 * /"));
		units.push(new Unit("ft_p_m_p_s", "feet per minute per second", "fpm/s", null, "m_p_s_s 5.08 0.001 * /"));
		units.push(new Unit("ft_p_s_s", "feet per second squared", "fps<sup>2</sup>", null, "m_p_s_s 0.3048 /"));
		units.push(new Unit("km_p_h_s", "kilometer per hour per second", "km/h/s", null, "m_p_s_s 0.2777778 /"));
		units.push(new Unit("knot_p_s", "knot per second", "kn/s", null, "m_p_s_s 0.514444 /"));
		units.push(new Unit("g", "standard gravity", "g", "Standard_gravity", "m_p_s_s 9.80665 /"));
		units.push(new Unit("ipss", "inch per second squared", "ips<sup>2</sup>", null, "m_p_s_s 0.0254 /"));
		units.push(new Unit("ipmps", "inch per minute per second", "ipm/s", null, "m_p_s_s 4.233333 0.0001 * /"));
		units.push(new Unit("mphps", "mile per hour per second", "mph/s", null, "m_p_s_s 0.44704 /"));
		this.categories.push(new UnitCategory("acceleration", "Acceleration", units));
	}
	
	//  TEMPERATURE
	{
		var units = new Array();
		units.push(new Unit("kelvin", "Kelvin", "K", "Kelvin", null));
		units.push(new Unit("celsius", "Celsius", "°C", "Celsius", "kelvin 273.15 -"));
		units.push(new Unit("fahrenheit", "Fahrenheit", "°F", "Fahrenheit", "kelvin 9 * 5 / 459.67 -"));
		units.push(new Unit("rankine", "Rankine", "°R", "Rankine_scale", "kelvin 9 * 5 /"));
		this.categories.push(new UnitCategory("temperature", "Temperature", units));
	}

	//  DENSITY
	{
		var units = new Array();
		units.push(new Unit("kg_p_m", "kilogram per cubic meter", "kg/m<sup>3</sup>", null, null));
		units.push(new Unit("oz_p_f", "ounce (avoirdupois) per cubic foot", "oz/ft<sup>3</sup>", null, "kg_p_m 1.001153961 /"));
		units.push(new Unit("oz_p_i", "ounce (avoirdupois) per cubic inch", "oz/in<sup>3</sup>", null, "kg_p_m 1729.994044 /"));
		units.push(new Unit("oz_p_gal", "ounce (avoirdupois) per gallon (Imperial)", "oz/gal", null, "kg_p_m 6.236023291 /"));
		units.push(new Unit("oz_p_gal_us", "ounce (avoirdupois) per gallon (U.S. fluid)", "oz/gal", null, "kg_p_m 7.489151707 /"));
		units.push(new Unit("lb_p_f", "pound (avoirdupois) per cubic foot", "lb/ft<sup>3</sup>", null, "kg_p_m 16.01846337 /"));
		units.push(new Unit("lb_p_i", "pound (avoirdupois) per cubic inch", "lb/in<sup>3</sup>", null, "kg_p_m 27679.90471 /"));
		units.push(new Unit("lb_p_gal", "pound (avoirdupois) per gallon (Imperial)", "lb/gal", null, "kg_p_m 99.77637266 /"));
		units.push(new Unit("lb_p_gal_us", "pound (avoirdupois) per gallon (U.S. fluid)", "lb/gal", null, "kg_p_m 119.8264273 /"));
		units.push(new Unit("slug_p_f", "slug per cubic foot", "slug/ft<sup>3</sup>", null, "kg_p_m 515.3788184 /"));
		this.categories.push(new UnitCategory("density", "Density", units));
	}
	
	//  ANGLE
	{
		var units = new Array();
		units.push(new Unit("rad", "radian", "rad", "Radian", null));
		units.push(new Unit("degree", "degree", "°", "Degree_(angle)", "rad 0.017453293 /"));
		units.push(new Unit("arcmin", "arcminute", "'", "Minute_of_arc", "rad 0.000290888 /"));
		units.push(new Unit("arcsec", "arcsecond", "\"", "Arcsecond", "rad 0.000004848137 /"));
		this.categories.push(new UnitCategory("plane_angle", "Plane angle", units));
	}

	//  INFORMATION
	{
		var units = new Array();
		units.push(new Unit("bit", "bit", "bit", "Bit", null));
		units.push(new Unit("byte", "byte", "B", "Byte", "bit 8 /"));
		units.push(new Unit("kbit", "kilobit", "kbit", "Kilobit", "bit 1024 /"));
		units.push(new Unit("kbyte", "kilobyte", "KB", "Kilobyte", "bit 8192 /"));
		units.push(new Unit("mbit", "megabit", "Mbit", "Megabit", "bit 1048576 /"));
		units.push(new Unit("mbyte", "megabyte", "MB", "Megabyte", "bit 8388608 /"));
		units.push(new Unit("gbit", "gigabit", "Gbit", "Gigabit", "bit 1073741824 /"));
		units.push(new Unit("gbyte", "gigabyte", "GB", "Gigabyte", "bit 8589934592 /"));
		units.push(new Unit("tbit", "terabit", "Tbit", "Terabit", "bit 1099511627776 /"));
		units.push(new Unit("tbyte", "terabyte", "TB", "Terabyte", "bit 8796093022208 /"));
		this.categories.push(new UnitCategory("information", "Information", units));
	}

	//  FUEL ECONOMY
	{
		var units = new Array();
		units.push(new Unit("km_p_l", "kilometer per liter", "km/L", null, null));
		units.push(new Unit("mpg_us", "mile per gallon (U.S.)", "mpg (US)", null, "km_p_l 0.425144 /"));
		units.push(new Unit("mpg_imp", "mile per gallon (Imperial)", "mpg (Imp)", null, "km_p_l 0.354013 /"));
		this.categories.push(new UnitCategory("fuel_economy", "Fuel Economy", units));
	}

	//  LIGHT
	{
		var units = new Array();
		units.push(new Unit("light_freq", "frequency", "Hz", "Hertz", null));
		units.push(new Unit("light_ev", "energy", "eV", "Electronvolt", "light_freq 0.00000000000000413566733 *"));
		units.push(new Unit("light_kjmol", "energy", "kJ/mol", "Joule_per_mole", "light_freq 0.000000000000399091897345 *"));
		units.push(new Unit("light_nm", "wave length", "nm", "Wavelength", "299792458000000000 light_freq /"));
		units.push(new Unit("light_wn", "wave number", "cm<sup>-1</sup>", "Wavenumber", "light_freq 29979245800 /"));
		this.categories.push(new UnitCategory("light", "Light", units));
	}

	this.currentCategoryIndex = 0;
}

UnitInformation.prototype.getCurrentCategory = function() {
	return this.categories[this.currentCategoryIndex];
}

UnitInformation.prototype.getCurrentCategoryDelta = function(delta) {
	var index = this.currentCategoryIndex;
	index += delta;
	while (index < 0) {
		index += this.categories.length;
	}
	index %= this.categories.length;
	return this.categories[index];
}

UnitInformation.prototype.setCurrentCategory = function(id) {
	for (var i = 0; i < this.categories.length; ++i) {
		var category = this.categories[i];
		if (category.id == id) {
			this.currentCategoryIndex = i;
			return;
		}
	}
	this.currentCategoryIndex = 0;
}
